package com.example.demo.interceptor;

import com.example.demo.annotation.RequiresPermission;
import com.example.demo.annotation.RequiresRole;
import com.example.demo.util.JwtUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 权限拦截器
 * 验证用户的角色和权限
 */
@Component
public class AuthorizationInterceptor implements HandlerInterceptor {
    
    private static final Logger log = LoggerFactory.getLogger(AuthorizationInterceptor.class);
    
    private final JwtUtil jwtUtil;
    private final ObjectMapper objectMapper;
    
    public AuthorizationInterceptor(JwtUtil jwtUtil, ObjectMapper objectMapper) {
        this.jwtUtil = jwtUtil;
        this.objectMapper = objectMapper;
    }
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        
        log.info("=== 权限拦截器被触发 === 请求路径: {} {}", request.getMethod(), request.getRequestURI());
        
        // 如果不是控制器方法，直接放行
        if (!(handler instanceof HandlerMethod)) {
            log.debug("非控制器方法，放行");
            return true;
        }
        
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        Class<?> clazz = handlerMethod.getBeanType();
        
        // 检查方法或类上是否有权限注解
        RequiresRole requiresRole = method.getAnnotation(RequiresRole.class);
        if (requiresRole == null) {
            requiresRole = clazz.getAnnotation(RequiresRole.class);
        }
        
        RequiresPermission requiresPermission = method.getAnnotation(RequiresPermission.class);
        if (requiresPermission == null) {
            requiresPermission = clazz.getAnnotation(RequiresPermission.class);
        }
        
        // 如果没有权限注解，直接放行
        if (requiresRole == null && requiresPermission == null) {
            return true;
        }
        
        // 获取请求头中的token
        String authHeader = request.getHeader("Authorization");
        log.info("🔍 检查Authorization头: [{}]", authHeader != null ? authHeader : "null");
        
        String token = jwtUtil.extractTokenFromHeader(authHeader);
        log.info("🔍 解析出的token: {}", token != null ? "***存在(长度:" + token.length() + ")***" : "null");
        
        if (!StringUtils.hasText(token)) {
            log.error("❌ 访问需要权限的接口但未提供token: {} {}, Authorization头: [{}]", 
                request.getMethod(), request.getRequestURI(), authHeader);
            sendUnauthorizedResponse(response, "未登录或登录已过期");
            return false;
        }
        
        // 验证token有效性
        if (!jwtUtil.validateToken(token)) {
            log.warn("无效的token: {} {}, token前10个字符: {}", 
                request.getMethod(), request.getRequestURI(), 
                token.length() > 10 ? token.substring(0, 10) + "..." : token);
            sendUnauthorizedResponse(response, "登录状态无效");
            return false;
        }
        
        // 获取用户信息
        Long userId = jwtUtil.getUserIdFromToken(token);
        String username = jwtUtil.getUsernameFromToken(token);
        List<String> userRoles = jwtUtil.getRolesFromToken(token);
        List<String> userPermissions = jwtUtil.getPermissionsFromToken(token);
        
        if (userId == null || username == null) {
            log.warn("从token中无法解析用户信息: {} {}", request.getMethod(), request.getRequestURI());
            sendUnauthorizedResponse(response, "用户信息无效");
            return false;
        }
        
        log.debug("用户 {} 访问接口: {} {}", username, request.getMethod(), request.getRequestURI());
        
        // 将用户信息设置到请求属性中，供控制器使用
        request.setAttribute("currentUserId", userId);
        request.setAttribute("currentUsername", username);
        request.setAttribute("currentUserRoles", userRoles);
        request.setAttribute("currentUserPermissions", userPermissions);
        
        // 验证角色
        if (requiresRole != null) {
            if (!checkRoles(requiresRole, userRoles)) {
                log.warn("用户 {} 访问接口 {} {} 权限不足，需要角色: {}", 
                    username, request.getMethod(), request.getRequestURI(), 
                    String.join(",", requiresRole.value()));
                sendForbiddenResponse(response, "权限不足，需要相应角色");
                return false;
            }
        }
        
        // 验证权限
        if (requiresPermission != null) {
            if (!checkPermissions(requiresPermission, userPermissions)) {
                log.warn("用户 {} 访问接口 {} {} 权限不足，需要权限: {}", 
                    username, request.getMethod(), request.getRequestURI(), 
                    String.join(",", requiresPermission.value()));
                sendForbiddenResponse(response, "权限不足，需要相应权限");
                return false;
            }
        }
        
        log.debug("用户 {} 权限验证通过", username);
        return true;
    }
    
    /**
     * 检查用户角色
     */
    private boolean checkRoles(RequiresRole requiresRole, List<String> userRoles) {
        if (userRoles == null || userRoles.isEmpty()) {
            return false;
        }
        
        String[] requiredRoles = requiresRole.value();
        RequiresRole.Logical logical = requiresRole.logical();
        
        if (logical == RequiresRole.Logical.AND) {
            // 需要所有角色
            for (String requiredRole : requiredRoles) {
                if (!userRoles.contains(requiredRole)) {
                    return false;
                }
            }
            return true;
        } else {
            // 需要任一角色
            for (String requiredRole : requiredRoles) {
                if (userRoles.contains(requiredRole)) {
                    return true;
                }
            }
            return false;
        }
    }
    
    /**
     * 检查用户权限
     */
    private boolean checkPermissions(RequiresPermission requiresPermission, List<String> userPermissions) {
        if (userPermissions == null || userPermissions.isEmpty()) {
            return false;
        }
        
        String[] requiredPermissions = requiresPermission.value();
        RequiresPermission.Logical logical = requiresPermission.logical();
        
        if (logical == RequiresPermission.Logical.AND) {
            // 需要所有权限
            for (String requiredPermission : requiredPermissions) {
                if (!userPermissions.contains(requiredPermission)) {
                    return false;
                }
            }
            return true;
        } else {
            // 需要任一权限
            for (String requiredPermission : requiredPermissions) {
                if (userPermissions.contains(requiredPermission)) {
                    return true;
                }
            }
            return false;
        }
    }
    
    /**
     * 发送未授权响应
     */
    private void sendUnauthorizedResponse(HttpServletResponse response, String message) throws IOException {
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.setContentType("application/json;charset=UTF-8");
        
        Map<String, Object> result = new HashMap<>();
        result.put("code", 401);
        result.put("message", message);
        result.put("data", null);
        
        try (PrintWriter writer = response.getWriter()) {
            writer.write(objectMapper.writeValueAsString(result));
            writer.flush();
        }
    }
    
    /**
     * 发送禁止访问响应
     */
    private void sendForbiddenResponse(HttpServletResponse response, String message) throws IOException {
        response.setStatus(HttpServletResponse.SC_FORBIDDEN);
        response.setContentType("application/json;charset=UTF-8");
        
        Map<String, Object> result = new HashMap<>();
        result.put("code", 403);
        result.put("message", message);
        result.put("data", null);
        
        try (PrintWriter writer = response.getWriter()) {
            writer.write(objectMapper.writeValueAsString(result));
            writer.flush();
        }
    }
}
